// 《围棋》作者版权所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package types2

import (
	"cmd/compile/internal/syntax"
	"fmt"
	"go/constant"
	"sort"
	"strconv"
	"strings"
	"unicode"
)

// declInfo描述包级别的常量、类型、变量或func声明。
type declInfo struct {
	file      *Scope           // 包含此声明的文件范围
	lhs       []*Var           // n:1变量声明的lhs，或nil 
	vtyp      syntax.Expr      // 类型，或nil（仅适用于常量和变量声明）
	init      syntax.Expr      // init/orig表达式，或nil（仅适用于常量和变量声明）
	inherited bool             // 如果设置，init表达式继承自上一个常量声明
	tdecl     *syntax.TypeDecl // 类型声明，或nil 
	fdecl     *syntax.FuncDecl // func声明，或nil 

	// deps字段跟踪初始化表达式依赖项。
	deps map[Object]bool // 延迟初始化
}

// hasInitializer报告声明的对象是否具有初始化
// 表达式或函数体。
func (d *declInfo) hasInitializer() bool {
	return d.init != nil || d.fdecl != nil && d.fdecl.Body != nil
}

// addDep将obj添加到d的init表达式所依赖的对象集中。
func (d *declInfo) addDep(obj Object) {
	m := d.deps
	if m == nil {
		m = make(map[Object]bool)
		d.deps = m
	}
	m[obj] = true
}

// arity检查常量或变量decl的lhs和rhs是否有匹配的名称和初始化值。
// 如果设置了继承，则初始化值来自
// 另一个（常量）声明。
func (check *Checker) arity(pos syntax.Pos, names []*syntax.Name, inits []syntax.Expr, constDecl, inherited bool) {
	l := len(names)
	r := len(inits)

	switch {
	case l < r:
		n := inits[l]
		if inherited {
			check.errorf(pos, "extra init expr at %s", n.Pos())
		} else {
			check.errorf(n, "extra init expr %s", n)
		}
	case l > r && (constDecl || r != 1): // 如果r==1，它可能是一个多值函数，我们还不能说什么
		n := names[r]
		check.errorf(n, "missing init expr for %s", n.Value)
	}
}

func validatedImportPath(path string) (string, error) {
	s, err := strconv.Unquote(path)
	if err != nil {
		return "", err
	}
	if s == "" {
		return "", fmt.Errorf("empty string")
	}
	const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD"
	for _, r := range s {
		if !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) {
			return s, fmt.Errorf("invalid character %#U", r)
		}
	}
	return s, nil
}

// declarePkgObj在包范围内声明obj，记录其ident->obj映射，
// 并更新检查。objMap。对象不能是函数或方法。
func (check *Checker) declarePkgObj(ident *syntax.Name, obj Object, d *declInfo) {
	assert(ident.Value == obj.Name())

	// 规范：“名为init 
	// 的包作用域或文件作用域标识符只能声明为具有此（func（））签名的函数。”
	if ident.Value == "init" {
		check.error(ident, "cannot declare init - must be func")
		return
	}

	// spec:“主包必须有包名main，并声明
	// 一个不带参数且不返回值的函数main。”
	if ident.Value == "main" && check.pkg.name == "main" {
		check.error(ident, "cannot declare main - must be func")
		return
	}

	check.declare(check.pkg.scope, ident, obj, nopos)
	check.objMap[obj] = d
	obj.setOrder(uint32(len(check.objMap)))
}

// filename返回适合调试输出的文件名。
func (check *Checker) filename(fileNo int) string {
	file := check.files[fileNo]
	if pos := file.Pos(); pos.IsKnown() {
		// 退回支票。fset。文件（pos）。Name（）
		// TODO（gri）我们需要实际的文件名吗？
		return pos.RelFilename()
	}
	return fmt.Sprintf("file[%d]", fileNo)
}

func (check *Checker) importPackage(pos syntax.Pos, path, dir string) *Package {
	// 如果我们已经为给定的（路径，目录）
	// 对准备了一个包，请使用它，而不是进行完整导入。
	// 检查程序。impMap仅缓存标记为Complete 
	// 或fake（失败导入的虚拟包）的包。不完整但
	// 非伪造软件包确实需要导入才能完成。
	key := importKey{path, dir}
	imp := check.impMap[key]
	if imp != nil {
		return imp
	}

	// 还没有包=>导入它
	if path == "C" && (check.conf.FakeImportC || check.conf.go115UsesCgo) {
		imp = NewPackage("C", "C")
		imp.fake = true // 包范围未填充
		imp.cgo = check.conf.go115UsesCgo
	} else {
		// 普通导入
		var err error
		if importer := check.conf.Importer; importer == nil {
			err = fmt.Errorf("Config.Importer not installed")
		} else if importerFrom, ok := importer.(ImporterFrom); ok {
			imp, err = importerFrom.ImportFrom(path, dir, 0)
			if imp == nil && err == nil {
				err = fmt.Errorf("Config.Importer.ImportFrom(%s, %s, 0) returned nil but no error", path, dir)
			}
		} else {
			imp, err = importer.Import(path)
			if imp == nil && err == nil {
				err = fmt.Errorf("Config.Importer.Import(%s) returned nil but no error", path)
			}
		}
		// 确保我们有一个有效的包名
		// （这里的错误只能通过创建后的包操作发生）
		if err == nil && imp != nil && (imp.name == "_" || imp.name == "") {
			err = fmt.Errorf("invalid package name: %q", imp.name)
			imp = nil // 在
		}
		if err != nil {
			check.errorf(pos, "could not import %s (%s)", path, err)
			if imp == nil {
				// 创建一个新的假包
				// 想出一个合理的包名（启发式）
				name := path
				if i := len(name); i > 0 && name[i-1] == '/' {
					name = name[:i-1]
				}
				if i := strings.LastIndex(name, "/"); i >= 0 {
					name = name[i+1:]
				}
				imp = NewPackage(path, name)
			}
			// 继续尽我们所能使用这个包
			imp.fake = true // 避免后续查找失败
		}
	}

	// 包应该是完整的或标记为假的，但一旦我们格式化了一次错误消息，就要小心
	if imp.complete || imp.fake {
		check.impMap[key] = imp
		// ，保持pkgPathMap 
		// 后续导入的最新信息。
		if check.pkgPathMap != nil {
			check.markImports(imp)
		}
		return imp
	}

	// 出现问题（进口商可能返回了不完整的包，没有错误）
	return nil
}

// collectObjects收集所有文件和包对象，并将它们插入各自的作用域中。它还执行导入，并将
// 方法与接收器基类型名称关联。
func (check *Checker) collectObjects() {
	pkg := check.pkg
	pkg.height = 0

	// pkgImports是迄今为止看到的任何包文件已经导入的包集。用于避免pkg中的重复条目。进口。分配并填充
	// 它（如果我们递增地检查测试文件，pkg.imports可能不是空的）。
	// 请注意，pkgImports由包（以及包路径）设置密钥，而不是由
	// importKey值设置密钥。两个不同的importKey值可能映射到同一个包
	// 这就是为什么我们不能使用检查。把地图放在这里。
	var pkgImports = make(map[*Package]bool)
	for _, imp := range pkg.imports {
		pkgImports[imp] = true
	}

	type methodInfo struct {
		obj  *Func        // 方法
		ptr  bool         // 如果指针接收器
		recv *syntax.Name // 接收器类型名称
	}
	var methods []methodInfo // 具有有效接收器和非空名称的收集方法
	var fileScopes []*Scope
	for fileNo, file := range check.files {
		// 包标识符表示当前包，
		// 但没有相应的包对象。
		check.recordDef(file.PkgName, nil)

		fileScope := NewScope(check.pkg.scope, syntax.StartPos(file), syntax.EndPos(file), check.filename(fileNo))
		fileScopes = append(fileScopes, fileScope)
		check.recordScope(file, fileScope)

		// 确定解析导入所需的文件目录
		// 文件名可能为“”（通常用于测试），在这种情况下
		// 我们得到“”作为我们想要的目录。
		fileDir := dir(file.PkgName.Pos().RelFilename()) // TODO（gri）这应该是文件名吗？

		first := -1                // 当前组中第一个常量的索引，或-1 
		var last *syntax.ConstDecl // last ConstDecl with init expressions，或nil 
		for index, decl := range file.DeclList {
			if _, ok := decl.(*syntax.ConstDecl); !ok {
				first = -1 // 我们不在一个常量声明中
			}

			switch s := decl.(type) {
			case *syntax.ImportDecl:
				// 导入包
				if s.Path == nil || s.Path.Bad {
					continue // 解析
				}
				path, err := validatedImportPath(s.Path.Value)
				if err != nil {
					check.errorf(s.Path, "invalid import path (%s)", err)
					continue
				}

				imp := check.importPackage(s.Path.Pos(), path, fileDir)
				if imp == nil {
					continue
				}

				if imp == Unsafe {
					// typecheck忽略导入的包对
					// 计算高度不安全。
					// TODO（mdempsky）：重新审视这一点。这似乎很好，但我不记得明确考虑过这个案子。
				} else if h := imp.height + 1; h > pkg.height {
					pkg.height = h
				}

				// local name overrides imported package name 
				name := imp.name
				if s.LocalPkgName != nil {
					name = s.LocalPkgName.Value
					if path == "C" {
						// match cmd/compile（规范未规定）
						check.error(s.LocalPkgName, `cannot rename import "C"`)
						continue
					}
				}

				if name == "init" {
					check.error(s, "cannot import package as init - init must be a func")
					continue
				}

				// add package to list of explicit imports 
				// （此功能为客户端提供方便
				// ；不需要进行类型检查）
				if !pkgImports[imp] {
					pkgImports[imp] = true
					pkg.imports = append(pkg.imports, imp)
				}

				pkgName := NewPkgName(s.Pos(), pkg, name, imp)
				if s.LocalPkgName != nil {
					// ，点代表程序包
					check.recordDef(s.LocalPkgName, pkgName)
				} else {
					check.recordImplicit(s, pkgName)
				}

				if path == "C" {
					// match cmd/compile（规范未规定）
					pkgName.used = true
				}

				// add import to file scope 
				check.imports = append(check.imports, pkgName)
				if name == "." {
					// dot import 
					if check.dotImportMap == nil {
						check.dotImportMap = make(map[dotImportKey]*PkgName)
					}
					// merge imported scope with file scope 
					for name, obj := range imp.scope.elems {
						// 注意：此处避免急于解析（名称，obj），因此我们仅
						// 根据需要解析dot导入的对象。

						// 包作用域可能包含未导出的对象，
						// 不要导入它们！
						if isExported(name) {
							// declare dot导入对象
							// （不要使用check.declare，因为它通过object.setScopePos修改对象
							// ，这会导致竞争条件；
							// 该对象可能同时导入多个文件范围
							// 。请参阅问题#32154。）
							if alt := fileScope.Lookup(name); alt != nil {
								var err error_
								err.errorf(s.LocalPkgName, "%s redeclared in this block", alt.Name())
								err.recordAltDecl(alt)
								check.report(&err)
							} else {
								fileScope.insert(name, obj)
								check.dotImportMap[dotImportKey{fileScope, name}] = pkgName
							}
						}
					}
				} else {
					// 在文件作用域中声明导入的包对象
					// （由于我们之前调用了check.recordDef，因此无需提供s.LocalPkgName）
					check.declare(fileScope, nil, pkgName, nopos)
				}

			case *syntax.ConstDecl:
				// iota是组
				if first < 0 || file.DeclList[index-1].(*syntax.ConstDecl).Group != s.Group {
					first = index
					last = nil
				}
				iota := constant.MakeInt64(int64(index - first))

				// 确定使用哪个初始化表达式
				inherited := true
				switch {
				case s.Type != nil || s.Values != nil:
					last = s
					inherited = false
				case last == nil:
					last = new(syntax.ConstDecl) // 确保最后存在
					inherited = false
				}

				// 声明所有常量
				values := unpackExpr(last.Values)
				for i, name := range s.NameList {
					obj := NewConst(name.Pos(), pkg, name.Value, nil, iota)

					var init syntax.Expr
					if i < len(values) {
						init = values[i]
					}

					d := &declInfo{file: fileScope, vtyp: last.Type, init: init, inherited: inherited}
					check.declarePkgObj(name, obj, d)
				}

				// 常量必须始终具有初始值。
				check.arity(s.Pos(), s.NameList, values, true, inherited)

			case *syntax.VarDecl:
				lhs := make([]*Var, len(s.NameList))
				// 如果只有一个rhs初始值设定项，则对所有lhs变量使用相同的declInfo d1 
				// 以便每个lhs变量依赖于相同的
				// rhs初始值设定项（n:1 var声明）。
				var d1 *declInfo
				if _, ok := s.Values.(*syntax.ListExpr); !ok {
					// lhs元素只在下面的for循环之后设置，
					// 但这没关系，因为declarePkgObj只收集declInfo 
					// 以备以后使用。
					d1 = &declInfo{file: fileScope, lhs: lhs, vtyp: s.Type, init: s.Values}
				}

				// 声明所有变量
				values := unpackExpr(s.Values)
				for i, name := range s.NameList {
					obj := NewVar(name.Pos(), pkg, name.Value, nil)
					lhs[i] = obj

					d := d1
					if d == nil {
						// 单个赋值
						var init syntax.Expr
						if i < len(values) {
							init = values[i]
						}
						d = &declInfo{file: fileScope, vtyp: s.Type, init: init}
					}

					check.declarePkgObj(name, obj, d)
				}

				// 如果没有类型，则必须有值。
				if s.Type == nil || values != nil {
					check.arity(s.Pos(), s.NameList, values, false, false)
				}

			case *syntax.TypeDecl:
				if len(s.TParamList) != 0 && !check.allowVersion(pkg, 1, 18) {
					check.versionErrorf(s.TParamList[0], "go1.18", "type parameter")
				}
				obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Value, nil)
				check.declarePkgObj(s.Name, obj, &declInfo{file: fileScope, tdecl: s})

			case *syntax.FuncDecl:
				name := s.Name.Value
				obj := NewFunc(s.Name.Pos(), pkg, name, nil)
				hasTParamError := false // 避免重复类型参数错误
				if s.Recv == nil {
					// 常规函数
					if name == "init" || name == "main" && pkg.name == "main" {
						if len(s.TParamList) != 0 {
							check.softErrorf(s.TParamList[0], "func %s must have no type parameters", name)
							hasTParamError = true
						}
						if t := s.Type; len(t.ParamList) != 0 || len(t.ResultList) != 0 {
							check.softErrorf(s, "func %s must have no arguments and no return values", name)
						}
					}
					// 不要在包作用域中声明init函数-它们是不可见的
					if name == "init" {
						obj.parent = pkg.scope
						check.recordDef(s.Name, obj)
						// init函数必须有一个主体
						if s.Body == nil {
							// TODO（gri）使此错误消息与上述
							check.softErrorf(obj.pos, "missing function body")
						}
					} else {
						check.declare(pkg.scope, s.Name, obj, nopos)
					}
				} else {
					// 方法
					// d.Recv！=nil 
					ptr, recv, _ := check.unpackRecv(s.Recv.Type, false)
					// 接收端无效的方法无法关联到类型，且从未找到具有空u名称的
					// 方法；不需要收集任何
					// 的。它们仍将与所有其他功能一起进行类型检查。
					if recv != nil && name != "_" {
						methods = append(methods, methodInfo{obj, ptr, recv})
					}
					check.recordDef(s.Name, obj)
				}
				if len(s.TParamList) != 0 && !check.allowVersion(pkg, 1, 18) && !hasTParamError {
					check.versionErrorf(s.TParamList[0], "go1.18", "type parameter")
				}
				info := &declInfo{file: fileScope, fdecl: s}
				// 方法不是包级别的对象，但我们仍然在
				// 对象映射中跟踪它们，以便我们可以像常规函数一样处理它们（如果
				// 接收器无效）；在将
				// 与下面的接收机基本类型关联时，我们还需要它们的fdecl信息。
				check.objMap[obj] = info
				obj.setOrder(uint32(len(check.objMap)))

			default:
				check.errorf(s, invalidAST+"unknown syntax.Decl node %T", s)
			}
		}
	}

	// 验证包和文件作用域中的对象具有不同的名称
	for _, scope := range fileScopes {
		for name, obj := range scope.elems {
			if alt := pkg.scope.Lookup(name); alt != nil {
				obj = resolve(name, obj)
				var err error_
				if pkg, ok := obj.(*PkgName); ok {
					err.errorf(alt, "%s already declared through import of %s", alt.Name(), pkg.Imported())
					err.recordAltDecl(pkg)
				} else {
					err.errorf(alt, "%s already declared through dot-import of %s", alt.Name(), obj.Pkg())
					// TODO（gri）点导入的对象没有位置；recordAltDecl不会打印任何内容
					err.recordAltDecl(obj)
				}
				check.report(&err)
			}
		}
	}

	// 现在我们有了所有包作用域对象和所有方法，
	// 尽可能将方法与接收器基类型名称关联起来。
	// 忽略具有无效接收器的方法。它们将是
	// 稍后进行类型检查，具有常规功能。
	if methods != nil {
		check.methods = make(map[*TypeName][]*Func)
		for i := range methods {
			m := &methods[i]
			// 确定接收器基本类型并将m与之关联。
			ptr, base := check.resolveBaseTypeName(m.ptr, m.recv)
			if base != nil {
				m.obj.hasPtrRecv_ = ptr
				check.methods[base] = append(check.methods[base], m.obj)
			}
		}
	}
}

// unpackRecv解压接收器类型并返回其组件：ptr指示
// rtyp是否为指针接收器，rname是接收器类型名称，tparams是其
// 类型参数（如果有）。仅当unpackParams为
// 集时，类型参数才被解包。如果rname为nil，则接收器不可用（即，源有一个我们
// 无法轻松解决的错误）。
func (check *Checker) unpackRecv(rtyp syntax.Expr, unpackParams bool) (ptr bool, rname *syntax.Name, tparams []*syntax.Name) {
L: // 解包接收器类型
	// 这接受
	// 在别处检查接收方表达式的有效性。
	for {
		switch t := rtyp.(type) {
		case *syntax.ParenExpr:
			rtyp = t.X
		// case*ast。StarExpr:
		// ptr=true 
		// rtyp=t.X 
		case *syntax.Operation:
			if t.Op != syntax.Mul || t.Y != nil {
				break
			}
			ptr = true
			rtyp = t.X
		default:
			break L
		}
	}

	// unpack类型参数，如果有
	if ptyp, _ := rtyp.(*syntax.IndexExpr); ptyp != nil {
		rtyp = ptyp.X
		if unpackParams {
			for _, arg := range unpackExpr(ptyp.Index) {
				var par *syntax.Name
				switch arg := arg.(type) {
				case *syntax.Name:
					par = arg
				case *syntax.BadExpr:
					// ignore-解析器已报告错误
				case nil:
					check.error(ptyp, invalidAST+"parameterized receiver contains nil parameters")
				default:
					check.errorf(arg, "receiver type parameter %s must be an identifier", arg)
				}
				if par == nil {
					par = syntax.NewName(arg.Pos(), "_")
				}
				tparams = append(tparams, par)
			}

		}
	}

	// unpack receiver name 
	if name, _ := rtyp.(*syntax.Name); name != nil {
		rname = name
	}

	return
}

// resolveBaseTypeName返回类型的非别名基类型名称，
// 是否有一个指向它的间接指针。在包作用域中，基类型名称必须声明为
// 且最多只能有一个指针间接寻址。如果不存在此类类型
// name，则返回的基数为零。
func (check *Checker) resolveBaseTypeName(seenPtr bool, typ syntax.Expr) (ptr bool, base *TypeName) {
	// 算法：从类型表达式（可能是名称）开始，
	// 我们通过别名声明跟踪该类型，直到到达
	// 非别名类型名称。如果我们遇到指针类型或
	// 括号之外的任何东西，我们就完成了。如果我们遇到多个指针类型
	// 我们就完成了。
	ptr = seenPtr
	var seen map[*TypeName]bool
	for {
		typ = unparen(typ)

		// 检查我们是否有指针类型
		// 如果pexpr，:=typ。（*ast.StarExpr）；pexpr！=nil{
		if pexpr, _ := typ.(*syntax.Operation); pexpr != nil && pexpr.Op == syntax.Mul && pexpr.Y == nil {
			// 如果我们已经看到了一个指针，那么我们就完成了
			if ptr {
				return false, nil
			}
			ptr = true
			typ = unparen(pexpr.X) // 继续使用指针基类型
		}

		// typ必须是一个名称
		name, _ := typ.(*syntax.Name)
		if name == nil {
			return false, nil
		}

		// name必须表示在当前包作用域
		// （注意点导入的对象不在包作用域中！）
		obj := check.pkg.scope.Lookup(name.Value)
		if obj == nil {
			return false, nil
		}

		// 对象必须是类型名。。。
		tname, _ := obj.(*TypeName)
		if tname == nil {
			return false, nil
		}

		// /。。。如果tdecl将tname定义为新类型
		if seen[tname] {
			return false, nil
		}

		// （而不是别名）
		tdecl := check.objMap[tname].tdecl // 必须存在于包范围内的对象
		if !tdecl.Alias {
			return ptr, tname
		}

		// 否则，继续解析
		typ = tdecl.Type
		if seen == nil {
			seen = make(map[*TypeName]bool)
		}
		seen[tname] = true
	}
}

// PackageObject类型检查所有包对象，但不检查函数体。
func (check *Checker) packageObjects() {
	// 按源代码顺序处理包对象以获得可复制的结果
	objList := make([]Object, len(check.objMap))
	i := 0
	for obj := range check.objMap {
		objList[i] = obj
		i++
	}
	sort.Sort(inSourceOrder(objList))

	// 向已检查类型添加新方法（来自之前的Checker.Files调用）
	for _, obj := range objList {
		if obj, _ := obj.(*TypeName); obj != nil && obj.typ != nil {
			check.collectMethods(obj)
		}
	}

	// 我们首先处理非别名类型声明，然后是别名声明、
	// 以及其他所有内容。这似乎可以避免大多数情况，即在别名可用之前需要使用
	// 类型。
	// 在某些情况下，这可能还不够好（另见第#25838期）。
	// 在这些情况下，请检查。ident将报告一个错误（“类型别名的使用无效”）。
	var aliasList []*TypeName
	var othersList []Object // 所有非类型的
	// 第一阶段：非别名类型声明
	for _, obj := range objList {
		if tname, _ := obj.(*TypeName); tname != nil {
			if check.objMap[tname].tdecl.Alias {
				aliasList = append(aliasList, tname)
			} else {
				check.objDecl(obj, nil)
			}
		} else {
			othersList = append(othersList, obj)
		}
	}
	// 第二阶段：别名类型声明
	for _, obj := range aliasList {
		check.objDecl(obj, nil)
	}
	// 第三阶段：所有其他声明
	for _, obj := range othersList {
		check.objDecl(obj, nil)
	}

	// 此时我们可能有一个非空检查。方法地图；这意味着并不是所有的
	// 条目都在typeDecl末尾被删除，因为没有找到相应的接收器基
	// 类型。在这种情况下，在声明这些
	// 方法时报告了一个错误。我们现在可以安全地丢弃这张地图了。
	check.methods = nil
}

// inSourceOrder实现排序。排序界面。
type inSourceOrder []Object

func (a inSourceOrder) Len() int           { return len(a) }
func (a inSourceOrder) Less(i, j int) bool { return a[i].order() < a[j].order() }
func (a inSourceOrder) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }

// 未使用的导入检查未使用的导入。
func (check *Checker) unusedImports() {
	// 如果未检查函数体，则可能缺少包的使用-不要检查
	if check.conf.IgnoreFuncBodies {
		return
	}

	// 规范：“这是非法的（…）直接导入包而不参考
	// 其导出的任何标识符。若要导入一个仅用于其副作用ABCDEG

	for _, obj := range check.imports {
		if !obj.used && obj.name != "_" {
			check.errorUnusedPkg(obj)
		}
	}
}

func (check *Checker) errorUnusedPkg(obj *PkgName) {
	// 如果导入的包的名称不是最终的
	// 导入路径元素，请在错误消息中显式显示它。
	// 注意，这将处理重命名的导入和包含非常规包声明的
	// 包的导入。
	// 注意，即使在Windows上，它也使用/始终使用b因为导入
	// 路径始终使用正斜杠。
	path := obj.imported.path
	elem := path
	if i := strings.LastIndex(elem, "/"); i >= 0 {
		elem = elem[i+1:]
	}
	if obj.name == "" || obj.name == "." || obj.name == elem {
		if check.conf.CompilerErrorMessages {
			check.softErrorf(obj, "imported and not used: %q", path)
		} else {
			check.softErrorf(obj, "%q imported but not used", path)
		}
	} else {
		if check.conf.CompilerErrorMessages {
			check.softErrorf(obj, "imported and not used: %q as %s", path, obj.name)
		} else {
			check.softErrorf(obj, "%q imported but not used as %s", path, obj.name)
		}
	}
}

// dir真诚地尝试返回路径的目录
// 部分。如果路径为空，则结果为“”。
// （根据go/build包依赖性测试，我们不能导入
// path/filepath，而只使用filepath.Dir。）
func dir(path string) string {
	if i := strings.LastIndexAny(path, `/\`); i > 0 {
		return path[:i]
	}
	// i<=0 
	return "."
}
